#pragma once
#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>

template <class T>
class RingQueue
{
public:
    RingQueue(int cap)
        : _queue(cap), _cap(cap), _producer_i(0), _consumer_i(0)
    {
        sem_init(&_room_sem, 0, cap);
        sem_init(&_data_sem, 0, 0);
        pthread_mutex_init(&_producer, nullptr);
        pthread_mutex_init(&_consumer, nullptr);
    }
    void push(const T &val)
    {
        sem_wait(&_room_sem);
        pthread_mutex_lock(&_producer);
        _queue[_producer_i++] = val;
        _producer_i %= _cap;
        pthread_mutex_unlock(&_producer);
        sem_post(&_data_sem);
    }
    void pop(T& x)
    {
        sem_wait(&_data_sem);
        pthread_mutex_lock(&_consumer);
        x = _queue[_consumer_i++];
        _consumer_i %= _cap;
        pthread_mutex_unlock(&_consumer);
        sem_post(&_room_sem);
    }
    ~RingQueue()
    {
        sem_destroy(&_room_sem);
        sem_destroy(&_data_sem);
        pthread_mutex_destroy(&_producer);
        pthread_mutex_destroy(&_consumer);
    }

private:
    std::vector<T> _queue;
    int _cap;
    int _producer_i;
    int _consumer_i;

    sem_t _room_sem;
    sem_t _data_sem;

    pthread_mutex_t _producer;
    pthread_mutex_t _consumer;
};
